/**
 * 1、debounce(防抖)和throttle(节流)的定义
 * 防抖：触发高频事件后n秒内函数只会执行一次，如果n秒内高频事件再次被触发，则重新计算时间。
 * 节流：高频事件触发，但在n秒内只会执行一次，所以节流会稀释函数的执行频率
 * 防抖是有条件的周期动作，而节流是没有条件的周期动作
 */

/**
 * 2、防抖和节流解决什么问题
 * 在进行resize、scroll、keyup、keydown、mousedown、mousemove等事件操作时，
 * 如果事件处理函数调用的频率无限制，
 * 会加重浏览器的负担，容易导致页面卡顿等影响用户的体验；
 * 这时就可以通过debounce和throttle函数来限制事件处理函数的调用频率，
 * 提升用户的体验，同时又不影响实际的效果
 */

/**
 * 3、防抖和节流的应用场景
 * 防抖：
 * 登录、发短信等按钮避免用户点击太快，以至于发送多次请求，需要防抖
 * 调整浏览器窗口大小时，resize次数过于频繁，造成计算过多
 * 文本编辑器实时保存，当无任何更改操作一秒后进行保存
 * dom元素的拖拽功能实现
 * 计算鼠标移动的距离
 * canvas模拟画板功能
 * 搜索联想
 * 
 * 节流
 * scroll事件，每隔一秒计算一次位置信息等
 * 浏览器播放事件，每隔一秒计算一次进度信息等
 * input框实时搜索并发送请求展示下拉列表，每隔一秒发送一次请求（也可做防抖）
 */

// 防抖代码的实现
function debounce(fn,wait,immediate){
    let timeout = null
    return function(){
        if(timeout){
            clearTimeout(timeout)
        }
        if(immediate){
            //立即触发版本
            let callNow = !timeout
            timeout = setTimeout(()=>{
                timeout = null
            }, wait)
            if(callNow) fn.apply(this,arguments)
        } else {
            timeout = setTimeout(()=>{
                fn.apply(this,arguments)
                timeout = null
            }, wait)
        }
    }
}

// 节流代码的实现
//基本逻辑是对timeout进行判断，只有为空才能设置setTimeout事件，从而达到了一个周期只执行一次回调函数的功能
function throttle(fn,wait,immediate){
    let timeout,previous = 0
    return function(){
        if(immediate){
            //立即执行版本
            let now = Date.now()
            if(now - previous > wait){
                fn.apply(this, arguments)
                previous = now
            }
        } else {
            if(!timeout){
                timeout = setTimeout(()=>{
                    clearTimeout(timeout)
                    timeout = null
                    fn.apply(this, arguments)
                }, wait)
            }
        }
    }
}

/**
 * 总结：函数节流与函数防抖都是为了限制函数的执行频次，都是一种性能优化的方法。
 * 区别是防抖是有条件的周期性动作，而节流是无条件的周期性动作。
 * 两者实现的核心都依赖于闭包
 */


/**针对以上timeout注意说明
 * timeout = null 只是将定时器的指向改为null，并没有在内存中清除定时器，
 * 定时器还是会如期运行；如同在debounce函数中将timeout = null 并不能达到防抖的目的，
 * 因为每个定时器都只是将内存地址指向了null，而
 * 每个定时器都将会执行一遍，而clearTimeout(timeout)会将定时器从内存中清除掉
 */